// Copyright Epic Games, Inc. All Rights Reserved.

#include "Settings/Widgets/LyraSettingsListEntrySetting_KeyboardInput.h"

#include "CommonUIExtensions.h"
#include "NativeGameplayTags.h"
#include "Settings/CustomSettings/LyraSettingKeyboardInput.h"
#include "UI/Foundation/LyraButtonBase.h"
#include "Widgets/Misc/GameSettingPressAnyKey.h"
#include "Widgets/Misc/KeyAlreadyBoundWarning.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraSettingsListEntrySetting_KeyboardInput)

#define LOCTEXT_NAMESPACE "LyraSettings"

UE_DEFINE_GAMEPLAY_TAG_STATIC(PressAnyKeyLayer, "UI.Layer.Modal"); // 定义模态UI层的游戏标签

/**
 * 设置此条目所代表的具体游戏设置
 * @param InSetting 要设置的游戏设置对象
 */
void ULyraSettingsListEntrySetting_KeyboardInput::SetSetting(UGameSetting* InSetting)
{
	KeyboardInputSetting = CastChecked<ULyraSettingKeyboardInput>(InSetting); // 将传入的设置转换为键盘输入设置类型

	Super::SetSetting(InSetting); // 调用父类设置方法

	Refresh(); // 刷新显示
}

/**
 * 当控件在原生端初始化时调用
 */
void ULyraSettingsListEntrySetting_KeyboardInput::NativeOnInitialized()
{
	Super::NativeOnInitialized(); // 调用父类初始化方法
	
	Button_PrimaryKey->OnClicked().AddUObject(this, &ThisClass::HandlePrimaryKeyClicked); // 绑定主键按钮点击事件
	Button_SecondaryKey->OnClicked().AddUObject(this, &ThisClass::HandleSecondaryKeyClicked); // 绑定副键按钮点击事件
	Button_Clear->OnClicked().AddUObject(this, &ThisClass::HandleClearClicked); // 绑定清除按钮点击事件
	Button_ResetToDefault->OnClicked().AddUObject(this, &ThisClass::HandleResetToDefaultClicked); // 绑定重置为默认值按钮点击事件
}

/**
 * 处理主键按钮点击事件
 */
void ULyraSettingsListEntrySetting_KeyboardInput::HandlePrimaryKeyClicked()
{
	// 创建并推送按下任意键面板到模态层
	UGameSettingPressAnyKey* PressAnyKeyPanel = CastChecked<UGameSettingPressAnyKey>(
		UCommonUIExtensions::PushContentToLayer_ForPlayer(GetOwningLocalPlayer(), PressAnyKeyLayer, PressAnyKeyPanelClass));
	PressAnyKeyPanel->OnKeySelected.AddUObject(this, &ThisClass::HandlePrimaryKeySelected, PressAnyKeyPanel); // 绑定键选择事件
	PressAnyKeyPanel->OnKeySelectionCanceled.AddUObject(this, &ThisClass::HandleKeySelectionCanceled, PressAnyKeyPanel); // 绑定选择取消事件
}

/**
 * 处理副键按钮点击事件
 */
void ULyraSettingsListEntrySetting_KeyboardInput::HandleSecondaryKeyClicked()
{
	// 创建并推送按下任意键面板到模态层
	UGameSettingPressAnyKey* PressAnyKeyPanel = CastChecked<UGameSettingPressAnyKey>(
		UCommonUIExtensions::PushContentToLayer_ForPlayer(GetOwningLocalPlayer(), PressAnyKeyLayer, PressAnyKeyPanelClass));
	PressAnyKeyPanel->OnKeySelected.AddUObject(this, &ThisClass::HandleSecondaryKeySelected, PressAnyKeyPanel); // 绑定键选择事件
	PressAnyKeyPanel->OnKeySelectionCanceled.AddUObject(this, &ThisClass::HandleKeySelectionCanceled, PressAnyKeyPanel); // 绑定选择取消事件
}

/**
 * 处理主键选择事件
 * @param InKey 选择的键
 * @param PressAnyKeyPanel 按下任意键面板
 */
void ULyraSettingsListEntrySetting_KeyboardInput::HandlePrimaryKeySelected(FKey InKey, UGameSettingPressAnyKey* PressAnyKeyPanel)
{
	PressAnyKeyPanel->OnKeySelected.RemoveAll(this); // 移除所有绑定到此对象的键选择事件
	ChangeBinding(0, InKey); // 更改绑定（槽位0为主键）
}

/**
 * 处理副键选择事件
 * @param InKey 选择的键
 * @param PressAnyKeyPanel 按下任意键面板
 */
void ULyraSettingsListEntrySetting_KeyboardInput::HandleSecondaryKeySelected(FKey InKey, UGameSettingPressAnyKey* PressAnyKeyPanel)
{
	PressAnyKeyPanel->OnKeySelected.RemoveAll(this); // 移除所有绑定到此对象的键选择事件
	ChangeBinding(1, InKey); // 更改绑定（槽位1为副键）
}

/**
 * 处理主键重复键选择事件（在键已绑定时）
 * @param InKey 选择的键
 * @param DuplicateKeyPressAnyKeyPanel 重复键警告面板
 */
void ULyraSettingsListEntrySetting_KeyboardInput::HandlePrimaryDuplicateKeySelected(FKey InKey, UKeyAlreadyBoundWarning* DuplicateKeyPressAnyKeyPanel) const
{
	DuplicateKeyPressAnyKeyPanel->OnKeySelected.RemoveAll(this); // 移除所有绑定到此对象的键选择事件
	KeyboardInputSetting->ChangeBinding(0, OriginalKeyToBind); // 使用原始键更改绑定
}

/**
 * 处理副键重复键选择事件（在键已绑定时）
 * @param InKey 选择的键
 * @param DuplicateKeyPressAnyKeyPanel 重复键警告面板
 */
void ULyraSettingsListEntrySetting_KeyboardInput::HandleSecondaryDuplicateKeySelected(FKey InKey, UKeyAlreadyBoundWarning* DuplicateKeyPressAnyKeyPanel) const
{
	DuplicateKeyPressAnyKeyPanel->OnKeySelected.RemoveAll(this); // 移除所有绑定到此对象的键选择事件
	KeyboardInputSetting->ChangeBinding(1, OriginalKeyToBind); // 使用原始键更改绑定
}

/**
 * 更改键位绑定
 * @param InKeyBindSlot 绑定槽位
 * @param InKey 要绑定的键
 */
void ULyraSettingsListEntrySetting_KeyboardInput::ChangeBinding(int32 InKeyBindSlot, FKey InKey)
{
	OriginalKeyToBind = InKey; // 记录原始要绑定的键
	TArray<FName> ActionsForKey; // 存储与此键关联的动作名称
	KeyboardInputSetting->GetAllMappedActionsFromKey(InKeyBindSlot, InKey, ActionsForKey); // 获取所有映射到此键的动作
	if (!ActionsForKey.IsEmpty()) // 如果此键已经绑定了其他动作
	{
		// 创建并推送键已绑定警告面板到模态层
		UKeyAlreadyBoundWarning* KeyAlreadyBoundWarning = CastChecked<UKeyAlreadyBoundWarning>(
		UCommonUIExtensions::PushContentToLayer_ForPlayer(GetOwningLocalPlayer(), PressAnyKeyLayer, KeyAlreadyBoundWarningPanelClass));

		FString ActionNames; // 用于拼接动作名称的字符串
		for (FName ActionName : ActionsForKey) // 遍历所有关联的动作
		{
			ActionNames += ActionName.ToString() += ", "; // 拼接动作名称
		}

		FFormatNamedArguments Args; // 格式化参数
		Args.Add(TEXT("InKey"), InKey.GetDisplayName()); // 添加键名参数
		Args.Add(TEXT("ActionNames"), FText::FromString(ActionNames)); // 添加动作名称参数

		// 设置警告文本
		KeyAlreadyBoundWarning->SetWarningText(FText::Format(LOCTEXT("WarningText", "{InKey} is already bound to {ActionNames} are you sure you want to rebind it?"), Args));
		// 设置取消文本
		KeyAlreadyBoundWarning->SetCancelText(FText::Format(LOCTEXT("CancelText", "Press escape to cancel, or press {InKey} again to confirm rebinding."), Args));

		if (InKeyBindSlot == 1) // 如果是副键槽位
		{
			KeyAlreadyBoundWarning->OnKeySelected.AddUObject(this, &ThisClass::HandleSecondaryDuplicateKeySelected, KeyAlreadyBoundWarning); // 绑定副键重复选择事件
		}
		else // 如果是主键槽位
		{
			KeyAlreadyBoundWarning->OnKeySelected.AddUObject(this, &ThisClass::HandlePrimaryDuplicateKeySelected, KeyAlreadyBoundWarning); // 绑定主键重复选择事件
		}
		KeyAlreadyBoundWarning->OnKeySelectionCanceled.AddUObject(this, &ThisClass::HandleKeySelectionCanceled, KeyAlreadyBoundWarning); // 绑定选择取消事件
	}
	else // 如果此键未被绑定
	{
		KeyboardInputSetting->ChangeBinding(InKeyBindSlot, InKey); // 直接更改绑定
	}
}

/**
 * 处理键选择取消事件（针对按下任意键面板）
 * @param PressAnyKeyPanel 按下任意键面板
 */
void ULyraSettingsListEntrySetting_KeyboardInput::HandleKeySelectionCanceled(UGameSettingPressAnyKey* PressAnyKeyPanel)
{
	PressAnyKeyPanel->OnKeySelectionCanceled.RemoveAll(this); // 移除所有绑定到此对象的选择取消事件
}

/**
 * 处理键选择取消事件（针对重复键警告面板）
 * @param PressAnyKeyPanel 重复键警告面板
 */
void ULyraSettingsListEntrySetting_KeyboardInput::HandleKeySelectionCanceled(UKeyAlreadyBoundWarning* PressAnyKeyPanel)
{
	PressAnyKeyPanel->OnKeySelectionCanceled.RemoveAll(this); // 移除所有绑定到此对象的选择取消事件
}

/**
 * 处理清除按钮点击事件
 */
void ULyraSettingsListEntrySetting_KeyboardInput::HandleClearClicked()
{
	KeyboardInputSetting->ChangeBinding(0, EKeys::Invalid); // 清除主键绑定
	KeyboardInputSetting->ChangeBinding(1, EKeys::Invalid); // 清除副键绑定
}

/**
 * 处理重置为默认值按钮点击事件
 */
void ULyraSettingsListEntrySetting_KeyboardInput::HandleResetToDefaultClicked()
{
	KeyboardInputSetting->ResetToDefault(); // 重置设置为默认值
}

/**
 * 当设置发生变化时调用
 */
void ULyraSettingsListEntrySetting_KeyboardInput::OnSettingChanged()
{
	Refresh(); // 刷新显示
}

/**
 * 刷新控件显示
 */
void ULyraSettingsListEntrySetting_KeyboardInput::Refresh()
{
	if (ensure(KeyboardInputSetting)) // 确保键盘输入设置有效
	{
		// 设置主键按钮文本
		Button_PrimaryKey->SetButtonText(KeyboardInputSetting->GetKeyTextFromSlot(EPlayerMappableKeySlot::First));
		// 设置副键按钮文本
		Button_SecondaryKey->SetButtonText(KeyboardInputSetting->GetKeyTextFromSlot(EPlayerMappableKeySlot::Second));
		
		// 只有当映射被自定义时才显示重置为默认值按钮
		if (ensure(Button_ResetToDefault)) // 确保重置按钮有效
		{
			if (KeyboardInputSetting->IsMappingCustomized()) // 如果映射已被自定义
			{
				Button_ResetToDefault->SetVisibility(ESlateVisibility::Visible); // 显示重置按钮
			}
			else
			{
				Button_ResetToDefault->SetVisibility(ESlateVisibility::Hidden); // 隐藏重置按钮
			}
		}		
	}
}

/**
 * 当列表条目被释放时调用
 */
void ULyraSettingsListEntrySetting_KeyboardInput::NativeOnEntryReleased()
{
	Super::NativeOnEntryReleased(); // 调用父类释放方法

	KeyboardInputSetting = nullptr; // 清空键盘输入设置指针
}

#undef LOCTEXT_NAMESPACE